/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include "sdb.h"

#define NR_WP 32


static WP wp_pool[NR_WP] = {};
static WP *head = NULL, *free_ = NULL;

void init_wp_pool() {
  int i;
  for (i = 0; i < NR_WP; i ++) {
    wp_pool[i].NO = i;
    wp_pool[i].is_using = false;
    wp_pool[i].next = (i == NR_WP - 1 ? NULL : &wp_pool[i + 1]);
  }

  head = NULL;
  free_ = wp_pool;
}

/* TODO: Implement the functionality of watchpoint */
WP* new_wp(){
  if(free_ == NULL)
    assert(0);
  WP* res = free_;
  free_ = res->next;

  res->is_using = true;

  if(head != NULL){
    head->previous = res;
  }
  res->next = head;
  head = res;
  return res;
}

void free_wp(WP* wp) {
  if(wp == NULL)
    return;
  if(!wp->is_using)
    return;
  //remove from used
  if(wp->previous != NULL)
    wp->previous->next = wp->next;
  else
    head = wp->next;
  if(wp->next != NULL)
    wp->next->previous = wp->previous;
  //clean wp
  wp->is_using = false;
  wp->previous = NULL;
  //add to free
  wp->next = free_;
  free_ = wp;
}

int free_wp_by_no(int num) {
  if(num >= 0 && num < NR_WP) {
    free_wp(&wp_pool[num]);
    return 0;
  }
  return 1;
}

void watchpoint_display() {
  WP *wp = head;
  while (wp != NULL) {
    printf("Watchpoint: NO %d: expr = \"%s\", val = %ld (0x%lX)\n", wp->NO, wp->expr, wp->val, wp->val);
    wp = wp->next;
  }
}

void watchpoint_scan() {
  WP *wp = head;
  uint64_t new_val;
  bool success = true;
  while (wp != NULL) {
    new_val = expr(wp->expr, &success);
    if(new_val != wp->val || !success){
      if (nemu_state.state == NEMU_RUNNING)
        nemu_state.state = NEMU_STOP;
      printf("Watchpoint triggered: NO %d: expr = \"%s\"\nold_val = %ld (0x%lX)\t\tnew_val = %ld (0x%lX)\n", wp->NO, wp->expr, wp->val, wp->val, new_val, new_val);
      wp->val = new_val;
    }
    wp = wp->next;
  }
}